<?php
/**
 * Created by PhpStorm.
 * User: yangos
 * Date: 18-6-17
 * Time: 下午7:08
 */

namespace vola;


class Hook {

    private $hooklist = [
        'action' => [],
        'filter' => []
    ];

    public function __construct()
    {
    }

    /**
     * 添加action
     * @param $name
     * @param string $function
     * @param int $level
     */
    public function action($name, $function = '', $level = -1) {

        if (is_array($name)) {
            $this->hooklist['action'] = array_merge($this->hooklist['action'], $name);
        } else {

            $temp = &$this->hooklist['action'];

            if (strpos($name, '-')) {
                $name = explode('-', $name);
                foreach ($name as $val) {
                    if (!isset($temp[$val])) {
                        $temp[$val] = [];
                    }
                    $temp = &$temp[$val];
                }
            } else {
                if (!isset($temp[$name])) {
                    $temp[$name] = [];
                }
                $temp = &$temp[$name];
            }

            if ($level === -1) {
                $temp[] = $function;
            } else {
                array_splice($temp, $level, 0, $function);
            }
            unset($temp);
        }
    }

    public function filter($name, $function = '', $level = -1) {
        if (is_array($name)) {
            $this->hooklist['filter'] = array_merge($this->hooklist['filter'], $name);
        } else {
            $temp = &$this->hooklist['filter'];

            if (strpos($name, '-')) {
                $name = explode('-', $name);
                foreach ($name as $val) {
                    if (!isset($temp[$val])) {
                        $temp[$val] = [];
                    }
                    $temp = &$temp[$val];
                }
            } else {
                if (!isset($temp[$name])) {
                    $temp[$name] = [];
                }
                $temp = &$temp[$name];
            }

            if ($level === -1) {
                $temp[] = $function;
            } else {
                array_splice($temp, $level, 0, $function);
            }
            unset($temp);
        }
    }

    /**
     * 执行钩子
     * @param string $name 名称
     * @param array $argument 参数
     * @param bool $return 是否返回
     * @return mixed|string
     */
    private function run($name, $argument = [], $return = false) {

        if ($return) {
            $list = $this->get_hook($name, 1);
        } else {
            $list = $this->get_hook($name, 0);
        }

        $data = '';

        foreach ($list as $item) {
            if (is_object($item)) {
                $data = call_user_func_array($item, $argument);
            } elseif (is_array($item)) {
                // 如果是class 使用容器加载
                $data = call_user_func_array([Container::getInstance()->make($item[0]), $item[1]], $argument);
            } else {
                // 使用容器执行函数
                $data = Container::getInstance()->runFunction($item, $argument);
            }
        }
        if ($return) {
            return $data;
        }
    }

    private function get_hook($name, $type = 1) {
        if ($type === 1) {
            $list = $this->hooklist['filter'];
        } else {
            $list = $this->hooklist['action'];
        }

        if (strpos($name, '-')) {
            $name = explode('-', $name);
            foreach ($name as $val) {
                if (!isset($list[$val])) {
                    return [];
                }
                $list = $list[$val];
            }
            return $list;
        } else {
            if (isset($list[$name])) {
                return $list[$name];
            }
        }
        return [];
    }

    /**
     * 执行action
     * @param string $name
     * @param mixed $argument
     */
    public function do_action($name, ...$argument) {
        $argument = empty($argument) ? [] : $argument[0];
        $this->run($name,$argument);
    }

    /**
     * 执行hook
     * @param string $name
     * @param mixed $argument
     * @return mixed|string
     */
    public function do_filter($name, ...$argument) {
        $argument = empty($argument) ? [] : $argument[0];
        return $this->run($name,$argument, true);
    }

    public function remove($name, $type = 1) {
        if ($type === 1) {
            $list = &$this->hooklist['filter'];
        } else {
            $list = &$this->hooklist['action'];
        }

        if (strpos($name, '-')) {
            $name = explode('-', $name);
            foreach ($name as $val) {
                if (!isset($list[$val])) {
                    return [];
                }
                $list = &$list[$val];
            }
            unset($list);
        } else {
            if (isset($list[$name])) {
                unset($list[$name]);
            }
        }
    }

    public function remove_filter() {

    }
}
